/***************************************************************************
                          octopus.c  -  description
                             -------------------
    begin                : Aug  2007
    author               : Benedikt Sauter, sauter@embedded-projects.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software, you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License           *
 *   version 2.1 as published by the Free Software Foundation.             *
 *                                                                         *
 ***************************************************************************/

/**
    \mainpage liboctopus API documentation

    Library to talk to octopus devices. You find the latest versions of liboctopus at
    http://www.embedded-projects.net/octopus

    The library is easy to use. Have a look at this short example:
    \include simple.c

    More examples can be found in the "examples" directory.
*/
/** \addtogroup liboctopus */
/* @{ */

#include <usb.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

#include "octopus.h"
#include "../../firmware/protocol.h"

#define F_CPU 16000000UL

#define octopus_error_return(code, str) do {  \
        octopus->error_str = str;             \
	return code;                       \
	} while(0);


/*
 * initial octopus handle before use octopus_open
 */
int octopus_init(struct octopus_context *octopus)
{
   if (octopus == NULL)
      octopus_error_return(-1,"octopus not valid");

   octopus->error_str = NULL;
   octopus->usb_handle = NULL;
  
   return 1;
}

/*
 * open first octopus on usb
 */
int octopus_open(struct octopus_context *octopus)
{
   if (octopus_open_id(octopus,VID,PID) < 0)
      octopus_error_return(-1,"could not find octopus device with pid and vid");
   
   return 1;
}

/*
 * open octopus with a given usb vendor and product id
 */
int octopus_open_id(struct octopus_context *octopus, int vendor, int product)
{
   struct usb_bus *busses;
   struct usb_bus *bus;
   struct usb_device *dev;

   usb_init();

   if (usb_find_busses() < 0)
      octopus_error_return(-1, "usb_find_busses() failed");
    
   if (usb_find_devices() < 0)
      octopus_error_return(-2, "usb_find_devices() failed");
  
   busses = usb_get_busses();
   
   if (busses == NULL)
      octopus_error_return(-3, "usb_get_busses() failed");
  
   for (bus = busses; bus; bus = bus->next)
   {
      for (dev = bus->devices; dev; dev = dev->next)
      {
         if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) 
         {
	         octopus_open_dev(octopus,dev);
	         return 1;
         }
      }
   }
   octopus_error_return(-4,"could not find octopus device with pid and vid");
   return -1;
}

/*
 * open octopus with a given usb name description (not tested!)
 */
int octopus_open_serial(struct octopus_context *octopus, char * serial)
{
   struct usb_bus *busses;
   struct usb_dev_handle* usb_handle;
   struct usb_bus *bus;
   struct usb_device *dev;
   char usbserial[64];
   int length=0;

   usb_init();

   if (usb_find_busses() < 0)
      octopus_error_return(-1, "usb_find_busses() failed");
   if (usb_find_devices() < 0)
      octopus_error_return(-2, "usb_find_devices() failed");

   busses = usb_get_busses();
   if(busses==NULL)
      octopus_error_return(-3, "usb_get_busses() failed");

   for (bus = busses; bus; bus = bus->next)
   {
      for (dev = bus->devices; dev; dev = dev->next)
      {
         usb_handle = usb_open(dev);
         usb_set_configuration(usb_handle,0);
         length = usb_get_string_simple(usb_handle,3,usbserial,64);
         usb_close(usb_handle);

         if (strncmp(serial,usbserial,length) == 0)
         {
            octopus_open_dev(octopus,dev);
            return 1;
         }
      }
   }
   
   octopus_error_return(-4,"could not found octopus device with serial number");
   return -1;
}

/*
 * open octopus with a libusb dev handle
 */
int octopus_open_dev(struct octopus_context *octopus, struct usb_device *dev)
{
   if (octopus == NULL)
      octopus_error_return(-1,"octopus handle is wrong or missing");

   if (dev == NULL)
      octopus_error_return(-2,"device handle is wrong or missing");

   if (!(octopus->usb_handle = usb_open(dev)))
      octopus_error_return(-3,"can't open usb device");

   if (usb_set_configuration (octopus->usb_handle,dev->config[0].bConfigurationValue) < 0)
      octopus_error_return(-4,"can't set configuration for given usb device");

   if (usb_claim_interface(octopus->usb_handle, 0)  < 0)
      octopus_error_return(-5,"can't claim interface for given usb device");

   if (usb_set_altinterface(octopus->usb_handle,0)  < 0)
      octopus_error_return(-6,"can't set altinterface for given usb device");

   return 1;
}

/*
 * close a open octopus handle
 */
int octopus_close(struct octopus_context *octopus)
{
   if (octopus == NULL)
      octopus_error_return(-1,"octopus is not a valid handle");

   if (usb_close(octopus->usb_handle) < 0)
      octopus_error_return(-2,usb_strerror());

   octopus->usb_handle = NULL;

   return 1;
}

/*
 * send message to octopus
 */
int octopus_message(struct  octopus_context *octopus, unsigned char *msg,
		    unsigned int msglen, unsigned char *answer, unsigned int answerlen)
{
   int timeout=0; 
   
   // msg[1] is length
   msg[1] = msglen;

   if (usb_bulk_write(octopus->usb_handle,1,(char *)msg,msglen,100) < msglen)
      octopus_error_return(-1,"transfer error occurred"); 

   if (answerlen>0)
   {
      while(1)
      {
         if(usb_bulk_read(octopus->usb_handle,0x81,(char*)answer,answerlen,100)>0)
            break; 
	 
         if(timeout++ >= 1000)
            octopus_error_return(-2,"receive error occurred");
      }

      switch(answer[1])
      {
        case RSP_OK: 
          return 1;
        case RSP_UNKOWN_CMD:
          octopus_error_return(-1,"unkown command"); 
        case RSP_UNKOWN_PIN:
          octopus_error_return(-2,"unkown pin"); 
        case RSP_WRONG_PIN_CONFIG:
         octopus_error_return(-3,"pin is in wrong configuration"); 
        case RSP_ERROR:
         octopus_error_return(-4,"error in usb device"); 
        case RSP_TIMEOUT:
         octopus_error_return(-4,"timeout error in usb device"); 
        default:
         octopus_error_return(-5,"unknown error"); 
      }
   }

   return 1;
}

/*
 * get internal device description of octopus (octopus_1, octocan_01)
 */
char *octopus_get_hwdesc(struct octopus_context *octopus, char *desc)
{
   int i;  
   unsigned char answer[64];
   unsigned char msg[2];
   
   msg[0] = CMD_GET_HW_ID;
  
   memset(answer, 0, sizeof(answer));

   if (octopus_message(octopus,msg,2,answer,14) == RSP_OK)
   {
      if ((int)answer[2] > 0)
         memcpy(desc,answer+3,(int)answer[2]);
      return desc;
   } 
   else 
      octopus_error_return(NULL,octopus->error_str);

   return desc;
}

/*
 * use pin as gpio
 */
int octopus_io_init(struct octopus_context *octopus, unsigned int pin)
{
   unsigned char answer[3];
   unsigned char msg[3];

   memset(answer, 0, sizeof(answer));
   memset(msg,    0, sizeof(msg));

   msg[0] = CMD_IO_INIT_PIN;
   msg[2] = pin;

   if (octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
      return 1;
   else return -1;
}


int octopus_io_init_port(struct octopus_context *octopus, unsigned int port)
{
  unsigned char msg[3];
  unsigned char answer[3];

  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_INIT_PORT;
  msg[2] = port;

  if(octopus_message(octopus,msg, sizeof(msg), answer, sizeof(answer))>0)
    return 1;
  else
    return -1;
}

int octopus_io_set_port_direction_out(struct octopus_context *octopus, unsigned int port, unsigned char mask)
{
  unsigned char answer[3];
  unsigned char msg[4];

  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));

  msg[0] = CMD_IO_PORT_DIRECTION_OUT;
  msg[2] = port;
  msg[3] = mask;
  
  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)))
    return 1;
  else 
    return -1;
}

int octopus_io_set_port_direction_in(struct octopus_context *octopus, unsigned int port, unsigned char mask)
{
  unsigned char answer[3];
  unsigned char msg[4];

  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PORT_DIRECTION_IN;
  msg[2] = port;
  msg[3] = mask;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)))
    return 1;
  else 
    return -1;
}


int octopus_io_set_port_direction_tri(struct octopus_context *octopus, unsigned int port, unsigned char mask)
{
  unsigned char answer[3];
  unsigned char msg[4];

  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PORT_DIRECTION_TRI;
  msg[2] = port;
  msg[3] = mask;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)))
    return 1;
  else return -1;
}

/*
 * use pin as output
 */
int octopus_io_set_pin_direction_out(struct octopus_context *octopus, unsigned int pin)
{
  unsigned char answer[3];
  unsigned char msg[3];
   
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PIN_DIRECTION_OUT;
  msg[2] = pin;

  if (octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) == RSP_OK)
     return 1;
  else 
     octopus_error_return(-1,octopus->error_str);
}


int octopus_io_set_pin_direction_in(struct octopus_context *octopus, unsigned int pin)
{
  unsigned char answer[3];
  unsigned char msg[3];

  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PIN_DIRECTION_IN;
  msg[2] = pin;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}



int octopus_io_set_pin_direction_tri(struct octopus_context *octopus, unsigned int pin)
{
  unsigned char answer[3];
  unsigned char msg[3];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));

  msg[0] = CMD_IO_PIN_DIRECTION_TRI;
  msg[2] = pin;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}

unsigned char octopus_io_get_port(struct octopus_context *octopus, unsigned int port)
{
  unsigned char answer[3];
  unsigned char msg[3];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PORT_GET;
  msg[2] = port;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))==RSP_OK)
    return (unsigned char) answer[2];
  else 
    return 0;
    //octopus_error_return((unsigned char)NULL,octopus->error_str);
}


int octopus_io_set_port(struct octopus_context *octopus, unsigned int port, unsigned char value)
{
  unsigned char answer[3];
  unsigned char msg[4];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PORT_SET;
  msg[2] = port;
  msg[3] = value;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)))
    return 1;
  else return -1;
}


int octopus_io_set_pin(struct octopus_context *octopus, unsigned int pin, unsigned int value)
{
  unsigned char answer[3];
  unsigned char msg[4];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PIN_SET;
  msg[2] = pin;
  msg[3] = value;
  
  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}  


int octopus_io_get_pin(struct octopus_context *octopus, unsigned int pin)
{
  unsigned char answer[3];
  unsigned char msg[3];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_IO_PIN_GET;
  msg[2] = pin;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))==RSP_OK)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);
}



/// part: adc
int octopus_adc_init(struct octopus_context *octopus, unsigned int pin)
{
  unsigned char answer[3];
  unsigned char msg[3];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_ADC_INIT_PIN;
  msg[2] = pin;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else
    return -1;
}

/*
int octopus_adc_deinit(struct octopus_context *octopus, int pin)
{
  unsigned char answer[3];
  unsigned char msg[] = {CMD_ADC_DEINIT_PIN,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,3)>0)
    return 1;
  else 
    return -1;
}
*/


int octopus_adc_get(struct octopus_context *octopus, unsigned int pin)
{
  unsigned char answer[5];
  unsigned char msg[3];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_ADC_GET;
  msg[2] =  pin;

  //XXX ????
  if (octopus_message (octopus, msg, sizeof(msg), answer, sizeof(answer)) >0)
  {
     unsigned int low  = (unsigned int)answer[3];
     unsigned int high = (unsigned int)answer[2];

     return((int)((unsigned int)(low | (high << 8))));
  }
  else
     return -1;
}

int octopus_adc_ref(struct octopus_context *octopus, unsigned int ref)
{
  unsigned char answer[3];
  unsigned char msg[3];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_ADC_REF;
  msg[2] = ref;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return (int)answer[1];
  else 
    octopus_error_return(-1,octopus->error_str);
}



int octopus_adc_get_bulk(struct octopus_context *octopus, 
  int pin, int number, int * buf)
{
  //XXX
  return 1;
}

int octopus_i2c_init(struct octopus_context *octopus)
{
  unsigned char answer[3];
  unsigned char msg[2];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_I2C_INIT;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else return -1;
}

int octopus_i2c_deinit(struct octopus_context *octopus)
{
  unsigned char answer[3];
  unsigned char msg[2];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_I2C_DEINIT;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else return -1;
}

int octopus_i2c_set_bitrate(struct octopus_context *octopus, int scl_freq)
{
  unsigned char answer[3];
  unsigned char msg[2];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_I2C_SET_BITRATE;

  //XXX rounding XXX
  int bitrate = ((F_CPU/scl_freq)-16) / 2;
  msg[2] = bitrate;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else return -1;
}

int octopus_i2c_send_defaults(struct octopus_context *octopus, int scl_freq, char address, char *data, int length)
{
  if(octopus_i2c_set_bitrate(octopus, scl_freq) <= 0)
    return -1;

  if(octopus_i2c_send_start(octopus) <= 0)
    return -2;

  if(octopus_i2c_send_byte(octopus, address) <= 0)
    return -3;

  if(octopus_i2c_send_bytes(octopus, data, length) <= 0)
    return -4;

  if(octopus_i2c_send_stop(octopus) <= 0)
    return -5;

  return 1;
}

int octopus_i2c_send_byte(struct octopus_context *octopus, char data)
{
  return octopus_i2c_send_bytes(octopus, &data, 1);
}

int octopus_i2c_send_bytes(struct octopus_context *octopus, char *data, int length)
{
  unsigned char answer[3];
  unsigned char msg[3+length];
  int i;

  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));

  if(length > 60)
    return -1; // max 60 bytes

  msg[0] = CMD_I2C_SEND;
  msg[2] = length;

  for(i = 0; i < length; i++)
    msg[3+i] = data[i];

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}



char octopus_i2c_receive_byte(struct octopus_context *octopus, int address)
{
  unsigned char c;

  if(octopus_i2c_recv(octopus,address,&c,1)>0)
    return c;
  else
    return -1;
}


int octopus_i2c_recv(struct octopus_context *octopus, int address, char *buf, int length)
{
  unsigned char answer[64];
  unsigned char msg[4];
  int i;

  if(length > 60)
    return -1; // max 60 bytes
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_I2C_RECV;
  msg[2] = address;
  msg[3] = length;


  if(octopus_message(octopus,msg,sizeof(msg),answer,length+3) > 0)
  {
    for(i = 0; i < length; i++)
      buf[i] = answer[i+2];
    return 1;
  }
  else 
    octopus_error_return(-1,octopus->error_str);
}

int octopus_i2c_send_start(struct octopus_context *octopus)
{
  unsigned char answer[3];
  unsigned char msg[2];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));
  
  msg[0] = CMD_I2C_SEND_START;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else return -1;

  return 1;
}

int octopus_i2c_send_stop(struct octopus_context *octopus)
{
  unsigned char answer[3];
  unsigned char msg[2];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));

  msg[0] = CMD_I2C_SEND_STOP;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else return -1;

  return 1;
}

/// part: SPI
int octopus_spi_init(struct octopus_context *octopus, int dord, int mode, int speed)
{
  unsigned char answer[3];
  unsigned char msg[5];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));

  msg[0] = CMD_SPI_INIT;
  msg[2] = dord;
  msg[3] = mode;
  msg[4] = speed;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else
    return -1;
}

int octopus_spi_deinit(struct octopus_context *octopus)
{
  unsigned char answer[3];
  unsigned char msg[2];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));

  msg[0] = CMD_SPI_DEINIT;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer))>0)
    return 1;
  else 
    return -1;
}


int octopus_spi_send(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60)
    return -1; // max 60 bytes
  
  unsigned char answer[2];
  unsigned char msg[64];
  
  memset(answer, 0, sizeof(answer));
  memset(msg,    0, sizeof(msg));

  msg[0] = CMD_SPI_SEND;
  msg[2] = (length);

  // build send array
  int i;
  for(i = 0; i < length; i++)
    msg[i+3]=buf[i];

  if(octopus_message(octopus,msg,3+length,answer,sizeof(answer))>0)
    return 1; 
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}

int octopus_spi_recv(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60)
    return -1; // max 60 bytes
  
  unsigned char answer[64];
  unsigned char msg[3];
  int i;

  msg[0] = CMD_SPI_RECV;
  msg[2] = (length);
  
  if(octopus_message(octopus,msg,2,answer,2+length)>0)
  {
    for(i = 0; i < length; i++)
      buf[i]=answer[i+2];
   
    return 1; 
  }
  else 
    octopus_error_return(-1,octopus->error_str);
}

int octopus_spi_send_and_recv(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60)
    return -1; // max 60 bytes
  
  unsigned char answer[2+length];
  unsigned char msg[66];
  int i;
  
  msg[0] = CMD_SPI_SEND_AND_RECV;
  msg[2] = (length);

  // build send array
  for(i = 0; i < length; i++)
    msg[i+3] = buf[i];

  if(octopus_message(octopus,msg,2+length,answer,2+length)>0)
  {
    for(i = 0; i < length; i++)
      buf[i] = answer[i+2];
 
    return 1; 
  }
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;

}

/// part:  Flash 93c46
int octopus_93c46_init(struct octopus_context *octopus)
{
   unsigned char answer[3];
   unsigned char msg[4];
   
   msg[0] = CMD_EXTERNAL_DEVICE;
   msg[2] = CMD_EXTERNAL_93C46;
   msg[3] = CMD_93C46_INIT;

   if(octopus_message(octopus,msg,4,answer,3)>0)
      return 1;
   else return -1;
}

int octopus_93c46_deinit(struct octopus_context *octopus)
{
   unsigned char answer[3];
   unsigned char msg[4];

   msg[0] = CMD_EXTERNAL_DEVICE;
   msg[2] = CMD_EXTERNAL_93C46;
   msg[3] = CMD_93C46_DEINIT;

   if (octopus_message(octopus,msg,4,answer,3) > 0)
      return 1;
   else return -1;
}

int octopus_93c46_write(struct octopus_context *octopus, unsigned char address, int length, unsigned char * buf)
{
   /*
   if(length>59){
      return -1; // max 60 bytes
   }
   */
   
   unsigned char answer[3];

   // cmd = cmd,len,timeout,address,data1,data2,..)
   unsigned char msg[150];
   msg[0] = CMD_EXTERNAL_DEVICE;
   msg[2] = CMD_EXTERNAL_93C46;
   msg[3] = CMD_93C46_WRITE;
   msg[4] = address;
   msg[5] = length;

   // build send array
   int i;
   for(i=0;i<length;i++)
      msg[i+6]=buf[i];

   if(octopus_message(octopus,msg,6+length,answer,3)>0)
      return 1; 
   else 
      octopus_error_return(-1,octopus->error_str);

   return 1;
}

int octopus_93c46_read(struct octopus_context *octopus, unsigned char address, int length, unsigned char * buf)
{
   /*
   if(length>59)
   {
      return -1; // max 60 bytes
   }*/

   unsigned char answer[150];
   unsigned char msg[6];
   
   msg[0] = CMD_EXTERNAL_DEVICE;
   msg[2] = CMD_EXTERNAL_93C46;
   msg[3] = CMD_93C46_READ;
   msg[4] = address;
   msg[5] = length;

   int i;

   if (octopus_message(octopus, msg, 6, answer, 2 + length) > 0)
   {    
      for(i=0;i<length;i++)
         buf[i]=answer[i+2];

      return 1; 
   }
   else 
      octopus_error_return(-1,octopus->error_str);
}


/// part:  PWM
int octopus_pwm_init(struct octopus_context *octopus, int pin)
{
   unsigned char answer[3];
   unsigned char msg[3];
   
   msg[0] = CMD_PWM_INIT_PIN;
   msg[2] = pin;

   if(octopus_message(octopus,msg,3,answer,3)>0)
      return 1;
   else return -1;
}

int octopus_pwm_deinit(struct octopus_context *octopus, int pin)
{
   unsigned char answer[3];
   unsigned char msg[3];
   
   msg[0] = CMD_PWM_DEINIT_PIN;
   msg[2] = pin;

   if(octopus_message(octopus,msg,3,answer,3)>0)
      return 1;
   else return -1;
}

int octopus_pwm_speed(struct octopus_context *octopus, int pin, int speed)
{
  unsigned char answer[3];
  unsigned char msg[4];
  
  msg[0] = CMD_PWM_SPEED;
  msg[2] = pin;
  msg[3] = speed;
  if(octopus_message(octopus,msg,4,answer,3)>0)
     return 1;
  else return -1;
}

int octopus_pwm_value(struct octopus_context *octopus, int pin, unsigned char value)
{
  unsigned char answer[3];
  unsigned char msg[4];
  
  msg[0] = CMD_PWM_VALUE;
  msg[2] = pin;
  msg[3] = value;

  if(octopus_message(octopus,msg,4,answer,3)>0)
     return 1;
  else return -1;
}

// uart
int octopus_uart_init(struct octopus_context *octopus, int uartport)
{
  unsigned char answer[3];
  unsigned char msg[3];
  
  msg[0] = CMD_UART_INIT;
  msg[2] = uartport;

  if(octopus_message(octopus,msg,3,answer,3) > 0)
    return 1;
  else return -1;
}

int octopus_uart_deinit(struct octopus_context *octopus, int uartport)
{
  unsigned char answer[3];
  unsigned char msg[3];
  
  msg[0] = CMD_UART_DEINIT;
  msg[2] = uartport;

  if(octopus_message(octopus,msg,3,answer,3) > 0)
    return 1;
  else return -1;
}

/* baudrate: baud rate (in bits per second) */
int octopus_uart_baudrate(struct octopus_context *octopus, int uartport, unsigned long int baudrate)
{
  unsigned int ubrr = F_CPU / 16UL / baudrate -1;
  unsigned char answer[3];
  unsigned char msg[5];
  
  msg[0] = CMD_UART_BAUDRATE;
  msg[2] = uartport;
  msg[3] = ubrr;      // UBRRnL
  msg[4] = ubrr >> 8; // UBRRnH

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else 
    return -1;
}

int octopus_uart_stopbits(struct octopus_context *octopus, int uartport, int stopbits)
{
  unsigned char answer[3];
  unsigned char msg[4];

  msg[0] = CMD_UART_STOPBITS;
  msg[2] = uartport;
  msg[3] = stopbits;

  if(octopus_message(octopus,msg,4,answer,3) > 0)
    return 1;
  else return -1;
}

int octopus_uart_databits(struct octopus_context *octopus, int uartport, int databits)
{
  unsigned char answer[3];
  unsigned char msg[4];

  msg[0] = CMD_UART_DATABITS;
  msg[2] = uartport;
  msg[3] = databits;

  if(octopus_message(octopus,msg,4,answer,3) > 0)
    return 1;
  else return -1;
}

int octopus_uart_parity(struct octopus_context *octopus, int uartport, char parity)
{
  unsigned char answer[3];
  unsigned char msg[4];

  msg[0] = CMD_UART_PARITY;
  msg[2] = uartport;
  msg[3] = parity;

  if(octopus_message(octopus,msg,4,answer,3) > 0)
    return 1;
  else return -1;
}

int octopus_uart_send(struct octopus_context *octopus, int uartport, char *data, int length)
{
  unsigned char answer[3];
  unsigned char msg[64];
  int i;

  // XXX 
  if(length > 60)
    return -1;

  msg[0] = CMD_UART_SEND;
  msg[2] = uartport;
  msg[3] = length;

  for(i = 0; i < length; i++)
    msg[4+i] = data[i];

  if(octopus_message(octopus,msg,length+4,answer,3) > 0)
    return 1;
  else return -1;
}


/*
int octopus_uart_putchar(struct octopus_context *octopus, int uartport, char data)
{
  return octopus_uart_send(octopus, uartport, &data, 1);
}
*/

/*
int octopus_uart_getchar(struct octopus_context *octopus, int uartport)
{
  unsigned char answer[3];
  unsigned char msg[] = {CMD_UART_GETCHAR,0x01,0x00};
  msg[2] = (char)uartport;

  if(octopus_message(octopus,msg,3,answer,4) > 0)
    return answer[2];
  else return -1;
}
*/

int octopus_uart_recv(struct octopus_context *octopus, int uartport, char *data, int length)
{
  int i;
  unsigned char answer[3];
  unsigned char msg[4];

  msg[0] = CMD_UART_RECV;
  msg[2] = uartport;
  msg[3] = length;

  if(octopus_message(octopus,msg,4,answer,3+length) > 0)
  {
    for(i = 0; i < length; i++) 
      data[i] = answer[2+i];
    
    return 1;
  }
  else
    return -1;
}

int octopus_uart_init_defaults(struct octopus_context *octopus, int uartport, unsigned long int baudrate, int databits, char parity, int stopbits)
{
  int status;

  status = octopus_uart_init(octopus, uartport);
  if(status <= 0)
    return -1;

  status = octopus_uart_baudrate(octopus, uartport, baudrate);
  if(status <= 0)
    return -2;

  status = octopus_uart_databits(octopus, uartport, databits);
  if(status <= 0)
    return -3;

  status = octopus_uart_parity(octopus, uartport, parity);
  if(status <= 0)
    return -4;

  status = octopus_uart_stopbits(octopus, uartport, stopbits);
  if(status <= 0)
    return -5;

  return 1;
}

int octopus_uart_init_default(struct octopus_context *octopus, int uartport)
{
  return octopus_uart_init_defaults(octopus, uartport, 9600, 8, 'N', 1);
};

// CAN
int octopus_can_init(struct octopus_context *octopus, unsigned int baudrate, unsigned int eid)
{
  unsigned char answer[3];
  unsigned char msg[4];

  msg[0] = CMD_CAN_INIT;
  msg[2] = baudrate;
  msg[3] = eid;// ? 0 : 1;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else
    return -1;
}

int octopus_can_send_remote(struct octopus_context *octopus, unsigned int mob)
{
  unsigned char answer[3];
  unsigned char msg[3];

  msg[0] = CMD_CAN_SEND_REMOTE;
  msg[2] = mob;

  if(mob < 0 || mob > 14)
    return -1;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else
    return -1;
}

int octopus_can_enable_mob(struct octopus_context *octopus, unsigned int mob, unsigned int mode, unsigned int id, unsigned int idm)
{
  unsigned char answer[3];
  unsigned char msg[12];

  msg[0]  = CMD_CAN_ENABLE_MOB;
  msg[2]  = mob;
  msg[3]  = mode;
  msg[4]  = (id >> 24);
  msg[5]  = (id >> 16);
  msg[6]  = (id >>  8);
  msg[7]  = (id);
  msg[8]  = (idm >> 24);
  msg[9]  = (idm >> 16);
  msg[10] = (idm >> 8);
  msg[11] = (idm);

  if(mob < 0 || mob > 14)
    return -1;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else
    return -1;
}

/* CAN-Bus silent mode */
int octopus_can_silent_mode(struct octopus_context *octopus, int mode) 
{
  unsigned char msg[3];
  unsigned char answer[3];

  msg[0] = CMD_CAN_SILENT_MODE;
  msg[2] = mode;

  return octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer));
}

int octopus_can_disable_mob(struct octopus_context *octopus, unsigned int mob)
{
  unsigned char answer[3];
  unsigned char msg[3];

  msg[0]  = CMD_CAN_DISABLE_MOB;
  msg[2]  = mob;

  if(mob < 0 || mob > 14)
    return -1;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else
    return -1;
}

int octopus_can_send_data(struct octopus_context *octopus, unsigned int mob, unsigned int id, unsigned int length, char *data)
{
  int i;
  unsigned char answer[3];
  unsigned char msg[16];

  msg[0] = CMD_CAN_SEND_DATA;
  msg[2] = mob;
  msg[3]  = (id  >> 24);
  msg[4]  = (id  >> 16);
  msg[5]  = (id  >>  8);
  msg[6]  = (id);
  msg[7] = length > 8 ? 8 : length;

  if(mob < 0 || mob > 14)
    return -1;

  for(i = 0; i < 8; i++)
    msg[8+i] = data[i];

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else
    return -1;
}

int octopus_can_receive_data(struct octopus_context *octopus, unsigned int mob, unsigned int *id, char *buf)
{
  int i;
  unsigned char answer[15];
  unsigned char msg[3];

  msg[0] = CMD_CAN_RECEIVE_DATA;
  msg[2] = mob;

  if(mob < 0 || mob > 14)
    return -1;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
  {
    *id = (answer[2])       +
          (answer[3] <<  8) +
          (answer[4] << 16) +
          (answer[5] << 24);
    
    for(i = 0; i < 8; i++)
      buf[i] = answer[6+i];
    return 1;
  }
  else
    return -1;
}

int octopus_can_deinit(struct octopus_context *octopus)
{
  unsigned char answer[3];
  unsigned char msg[2];
  
  msg[0] = CMD_CAN_DEINIT;
  msg[1] = 0;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else
    return -1;
}


int octopus_can_set_autoreply(struct octopus_context *octopus, unsigned int mob, unsigned int length, char *data)
{
  int i;
  unsigned char answer[3];
  unsigned char msg[12];

  msg[0] = CMD_CAN_SET_AUTOREPLY;
  msg[2] = mob;
  msg[3] = length > 8 ? 8 : length;

  for(i = 0; i < 8; i++)
    msg[4+i] = data[i];

  if(mob < 0 || mob > 14)
    return -1;

  if(octopus_message(octopus,msg,sizeof(msg),answer,sizeof(answer)) > 0)
    return 1;
  else
    return -1;
}

/*
 * EEPROM
 */

int octopus_eeprom_write_bytes(struct octopus_context *octopus, unsigned int addr, char *buf, unsigned int length)
{
  int i;
  unsigned char msg[64];
  unsigned char answer[3];

  msg[0] = CMD_EEPROM_WRITE_BYTES;
  msg[2] = (addr);
  msg[3] = (addr << 8);
  msg[4] = (length);
  msg[5] = (length << 8);

  for(i = 0; i < length; i++)
    msg[6+i] = buf[i];

  msg[6+length] = 0;

  if(octopus_message(octopus,msg,7+length,answer,3) > 0)
    return 1;
  else
    return -1;
}

int octopus_eeprom_read_bytes(struct octopus_context *octopus, unsigned int addr, char *buf, unsigned int length)
{
  int i;
  unsigned char msg[6];
  unsigned char answer[68];

  msg[0] = CMD_EEPROM_READ_BYTES;
  msg[2] = (addr);
  msg[3] = (addr << 8);
  msg[4] = (length);
  msg[5] = (length << 8);
  msg[6] = 0;

  if(octopus_message(octopus,msg,7,answer,3+length) > 0)
  {
    for(i = 0; i < length; i++)
      buf[i] = answer[2+i];

    return 1;
  }
  else
    return -1;
}
